package ewelinkapi

import (
	"bytes"
	"context"
	"errors"
	"io"
	"net/http"

	"gitee.com/libs_2/ewelinkapi/constant"
	"gitee.com/libs_2/ewelinkapi/utils"
	"github.com/rs/zerolog/log"
)

func (c *Client) doPost(ctx context.Context, url string, needAppId, needToken, needSign bool, body []byte) ([]byte, error) {
	return c.doRequest(ctx, url, "POST", needAppId, needToken, needSign, body)
}

func (c *Client) doGet(ctx context.Context, url string, needAppId, needToken, needSign bool) ([]byte, error) {
	return c.doRequest(ctx, url, "GET", needAppId, needToken, needSign, nil)
}

func (c *Client) doDelete(ctx context.Context, url string, needAppId, needToken, needSign bool) ([]byte, error) {
	return c.doRequest(ctx, url, "DELETE", needAppId, needToken, needSign, nil)
}

// 发送post请求
//
//	url: 表示接口path(不含域名)
//	needAppId: 是否需要附加appId,目前仅有User 模块的接口需要
//	needToken: 是否需要附加access_token
//	needSign: 是否需要附加sign，和needToken 二选一为true
func (c *Client) doRequest(ctx context.Context, url string, method string, needAppId, needToken, needSign bool, body []byte) ([]byte, error) {
	var d io.Reader
	if body != nil {
		d = bytes.NewReader(body)
	}
	req, err := http.NewRequest(method, c.domain+url, d)
	if err != nil {
		return nil, err
	}

	req.Header.Set("X-CK-Nonce", utils.BuildNonce())
	req.Header.Set("Package-Name", "com.coolkit")
	if needAppId {
		req.Header.Set("X-CK-Appid", c.appId)
	}

	if needToken {
		var token string
		if len(c.tmpToken) > 0 {
			token = c.tmpToken
		} else {
			token, err = c.getToken(ctx)
			if err != nil {
				return nil, err
			}
		}

		req.Header.Set("Authorization", "Bearer "+token)
	}
	if needSign {
		sign, err := utils.SHA256(body, c.secret)
		if err != nil {
			return nil, err
		}
		req.Header.Set("Authorization", "Sign "+sign)
	}

	req.Header.Set("Content-Type", "application/json")
	req.Header.Set("User-Agent", "okhttp/3.12.1")

	res, err := http.DefaultClient.Do(req)
	if err != nil {
		return nil, err
	}

	defer res.Body.Close()

	// dec := json.NewDecoder(res.Body)
	// err = dec.Decode(&cr)
	// if err != nil {
	// 	return cr, err
	// }

	data, err := io.ReadAll(res.Body)
	if err != nil {
		return nil, err
	}

	return data, nil
}

// 获取token ,内部包含自动更新token逻辑
func (c *Client) getToken(ctx context.Context) (string, error) {
	// 先尝试获取access token
	at := c.cache.GetAccessToken(ctx, constant.KEY_ACCESS_TOKEN)
	if len(at) > 0 {
		return at, nil
	}

	// access_token 可能已经过期，尝试获取 refresh_token来刷新
	rt := c.cache.GetRefreshToken(ctx, constant.KEY_REFRESH_TOKEN)
	if len(rt) > 0 {
		data, err := c.RefreshToken(ctx, rt)
		if err != nil {
			return "", err
		}

		if len(data.AccessToken) > 0 {
			err = c.cache.SetAccessToken(ctx, constant.KEY_ACCESS_TOKEN, data.AccessToken)
			if err != nil {
				log.Error().Err(err).Msg("auto token failed on SetAccessToken")
			}

			err = c.cache.SetRefreshToken(ctx, constant.KEY_REFRESH_TOKEN, data.RefreshToken)
			if err != nil {
				log.Error().Err(err).Msg("auto token failed on SetAccessToken")
			}

			return data.AccessToken, nil
		}
	}

	// refresh_token 也失效，尝试登录
	if !c.autoToken {
		return "", errors.New("token已经失效")
	}

	data, err := c.LoginAccount(ctx, c.accountReq)
	if err != nil {
		log.Error().Err(err).Interface("accountReq", c.accountReq).
			Msg("auto token failed on LoginAccount")
		return "", err
	}
	if len(data.AccessToken) > 0 {
		err = c.cache.SetAccessToken(ctx, constant.KEY_ACCESS_TOKEN, data.AccessToken)
		if err != nil {
			log.Error().Err(err).Msg("auto token failed on SetAccessToken")
		}

		err = c.cache.SetRefreshToken(ctx, constant.KEY_REFRESH_TOKEN, data.RefreshToken)
		if err != nil {
			log.Error().Err(err).Msg("auto token failed on SetAccessToken")
		}

		return data.AccessToken, nil
	}
	return "", nil
}
